package com.morningstar.flow.controller;

import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.dev33.satoken.stp.StpUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.morningstar.common.dao.entity.CommonQuery;
import com.morningstar.common.result.Result;
import com.morningstar.flow.controller.request.CompleteTask;
import com.morningstar.flow.controller.result.FlowTask;
import com.morningstar.flow.controller.result.FlowTaskDetail;
import com.morningstar.flow.dao.entity.FlowInstance;
import com.morningstar.flow.dao.entity.FlowNode;
import com.morningstar.flow.dao.entity.FlowProcess;
import com.morningstar.flow.service.FlowInstanceService;
import com.morningstar.flow.service.FlowNodeService;
import com.morningstar.flow.service.FlowProcessService;
import com.morningstar.flow.service.FlowTaskService;
import com.morningstar.system.api.feign.ISystemUserFeign;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.flowable.engine.HistoryService;
import org.flowable.engine.TaskService;
import org.flowable.task.api.Task;
import org.flowable.task.api.TaskInfo;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.springframework.web.bind.annotation.*;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static com.morningstar.common.constants.permission.PermissionConstants.DETAIL;

@Slf4j
@RestController
@RequestMapping("/flow/task")
public class FlowTaskController {

    private static final String PERMISSION = "flow.task";
    private static final String PERMISSION_DETAIL = PERMISSION + DETAIL;
    private static final String PERMISSION_COMPLETE = PERMISSION + ".complete";
    private static final String PERMISSION_MY_TODO = PERMISSION + ".my.todo";
    private static final String PERMISSION_MY_DONE = PERMISSION + ".my.done";

    @Resource
    private TaskService taskService;

    @Resource
    private HistoryService historyService;

    @Resource
    private FlowProcessService flowProcessService;

    @Resource
    private FlowInstanceService flowInstanceService;

    @Resource
    private FlowNodeService flowNodeService;

    @Resource
    private FlowTaskService flowTaskService;

    @Resource
    private ISystemUserFeign systemUserFeign;

    /**
     * 查询我的代办
     */
    @PostMapping("/myTodo")
    @SaCheckPermission(PERMISSION_MY_TODO)
    public Result<Page<FlowTask>> myTodo(@RequestBody CommonQuery<FlowTask, FlowTask> commonQuery) {
        int size = (int) commonQuery.getPage().getSize();
        int pages = (int) commonQuery.getPage().getCurrent();
        String assign = StpUtil.getLoginId().toString();
        List<Task> tasks = taskService.createTaskQuery()
                .taskAssignee(assign)
                .orderByTaskCreateTime()
                .desc()
                .listPage((pages - 1) * size, size);
        long count = taskService.createTaskQuery().taskAssignee(assign).count();
        dealData(tasks, pages, size, count);
        return Result.successData(dealData(tasks, pages, size, count));
    }

    /**
     * 查询我的已办 任务
     *
     * @return 我的已办
     */
    @PostMapping("/myDone")
    @SaCheckPermission(PERMISSION_MY_DONE)
    public Result<Page<FlowTask>> myDone(@RequestBody CommonQuery<FlowTask, FlowTask> commonQuery) {
        int size = (int) commonQuery.getPage().getSize();
        int pages = (int) commonQuery.getPage().getCurrent();
        String assign = StpUtil.getLoginId().toString();
        List<HistoricTaskInstance> tasks = historyService.createHistoricTaskInstanceQuery()
                .taskAssignee(assign)
                .finished()
                .taskWithoutDeleteReason()
                .orderByHistoricTaskInstanceEndTime()
                .desc()
                .listPage((pages - 1) * size, size);
        long count = historyService.createHistoricTaskInstanceQuery()
                .taskAssignee(assign).count();
        dealData(tasks, pages, size, count);
        return Result.successData(dealData(tasks, pages, size, count));
    }

    /**
     * 获取当前任务信息
     *
     * @param taskId 任务Id
     * @return 任务信息
     */
    @GetMapping("/detail/{taskId}")
    @SaCheckPermission(PERMISSION_DETAIL)
    public Result<FlowTaskDetail> detail(@PathVariable("taskId") String taskId) {
        TaskInfo taskInfo;
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
        if (task != null) {
            taskInfo = task;
        } else {
            taskInfo = historyService.createHistoricTaskInstanceQuery().taskId(taskId).singleResult();
        }
        if (taskInfo == null) {
            return Result.fail("任务不存在");
        }
        Map<Long, String> userIdAndName = systemUserFeign.selectUserIdAndName();
        FlowInstance flowInstance = flowInstanceService.queryByProcessInstanceId(taskInfo.getProcessInstanceId());
        FlowProcess flowProcess = flowProcessService.getById(flowInstance.getProcessId());
        FlowNode flowNode = flowNodeService.selectByDefinitionId(taskInfo.getProcessDefinitionId(), taskInfo.getTaskDefinitionKey());
        FlowTaskDetail flowTaskDetail = this.getFlowTask(taskInfo, userIdAndName, flowInstance, flowProcess);
        flowTaskDetail.setFlowInstance(flowInstance);
        flowTaskDetail.setFlowProcess(flowProcess);
        flowTaskDetail.setFlowNode(flowNode);
        return Result.successData(flowTaskDetail);
    }

    /**
     * 完成任务
     *
     * @param completeTask 请求参数
     * @return 基本结果类
     */
    @PostMapping("/complete")
    @SaCheckPermission(PERMISSION_COMPLETE)
    public Result<Object> complete(@RequestBody CompleteTask completeTask) {
        return flowTaskService.complete(completeTask);
    }

    /**
     * 处理数据
     *
     * @param taskInfos 任务信息
     * @param pages     页码
     * @param size      页数
     * @param count     统计
     * @return 分页类
     */
    private Page<FlowTask> dealData(List<? extends TaskInfo> taskInfos, long pages, long size, long count) {
        List<String> instanceIds = taskInfos.stream().map(TaskInfo::getProcessInstanceId).toList();
        Map<String, FlowInstance> map = flowInstanceService.queryMapByInstanceIds(instanceIds);
        Map<Long, FlowProcess> flowProcessMap = flowProcessService.list()
                .stream()
                .collect(Collectors.toMap(FlowProcess::getProcessId, item -> item));
        Map<Long, String> userIdAndName = systemUserFeign.selectUserIdAndName();
        List<FlowTask> flowTasks = new ArrayList<>(taskInfos.size());
        for (TaskInfo task : taskInfos) {
            FlowInstance flowInstance = map.get(task.getProcessInstanceId());
            FlowProcess flowProcess = flowProcessMap.get(flowInstance.getProcessId());
            FlowTask flowTask = this.getFlowTask(task, userIdAndName, flowInstance, flowProcess);
            flowTasks.add(flowTask);
        }
        Page<FlowTask> page = new Page<>(pages, size, count);
        page.setRecords(flowTasks);
        return page;
    }


    private FlowTaskDetail getFlowTask(TaskInfo task, Map<Long, String> userIdAndName,
                                       FlowInstance flowInstance, FlowProcess flowProcess) {
        FlowTaskDetail flowTask = new FlowTaskDetail();
        flowTask.setTaskId(task.getId());
        flowTask.setTaskName(task.getName());
        flowTask.setTaskDefinitionKey(task.getTaskDefinitionKey());
        flowTask.setAssignee(task.getAssignee());
        flowTask.setAssigneeName(userIdAndName.get(Long.parseLong(task.getAssignee())));
        flowTask.setCreateTime(task.getCreateTime());
        if (task instanceof HistoricTaskInstance historicTaskInstance) {
            flowTask.setEndTime(historicTaskInstance.getEndTime());
        }
        flowTask.setProcessInstanceId(task.getProcessInstanceId());
        if (flowInstance != null) {
            flowTask.setBizNumber(flowInstance.getBizNumber());
            flowTask.setProcessName(flowProcess.getProcessName());
            flowTask.setAssigneeName(userIdAndName.get(Long.valueOf(task.getAssignee())));
            flowTask.setStartUserId(flowInstance.getStartUserId().toString());
            flowTask.setStartUserName(userIdAndName.get(flowInstance.getStartUserId()));
        }
        return flowTask;
    }

}
